package com.jfs.observer.request.abstracts;

import com.jfs.constant.CommonConstant;
import com.jfs.constant.SipConstant;
import com.jfs.enums.SipMethodEnum;
import com.jfs.properties.SipProperties;
import com.jfs.utils.MD5Util;
import gov.nist.javax.sip.SipStackImpl;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.stack.SIPServerTransaction;
import lombok.extern.slf4j.Slf4j;

import javax.sip.*;
import javax.sip.address.AddressFactory;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
import javax.sip.header.*;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Objects;

/**
 * @Description 处理者公用方法抽象类
 * @E-mail cwm3412638@163.com
 * @Author cwm
 * @Date 2022-08-23
 */
@Slf4j
public class AbstractProcessor {
    protected final SipFactory sipFactory;
    protected final SipProvider tcpSipProvider;
    protected final SipProvider udpSipProvider;

    protected final SipProperties sipProperties;

    public AbstractProcessor(SipFactory sipFactory, SipProvider tcpSipProvider, SipProvider udpSipProvider, SipProperties sipProperties) {
        this.sipFactory = sipFactory;
        this.tcpSipProvider = tcpSipProvider;
        this.udpSipProvider = udpSipProvider;
        this.sipProperties = sipProperties;
    }

    /**
     * 获取sip信令服务器密码
     *
     * @return
     */
    public String getSipPassword() {
        return this.sipProperties.getPassword();
    }

    /**
     * 获取请求头构造工厂
     *
     * @return
     * @throws PeerUnavailableException
     */
    public HeaderFactory getHeaderFactory() throws PeerUnavailableException {
        return this.sipFactory.createHeaderFactory();
    }

    /**
     * 获取消息构造工厂
     *
     * @return
     * @throws PeerUnavailableException
     */
    public MessageFactory getMessageFactory() throws PeerUnavailableException {
        return this.sipFactory.createMessageFactory();
    }

    /**
     * 获取地址构造工厂
     *
     * @return
     * @throws PeerUnavailableException
     */
    public AddressFactory getAddressFactory() throws PeerUnavailableException {
        return this.sipFactory.createAddressFactory();
    }

    /**
     * 获取请求ip地址
     * 如果是在内网环境下获取的是内网真实ip 如果是通过路由转发 则是路由器转发出去公网地址
     *
     * @param requestEvent
     * @return
     */
    public String getIpByRequest(RequestEvent requestEvent) {
        ViaHeader header = (ViaHeader) requestEvent.getRequest().getHeader(ViaHeader.NAME);
        if (Objects.nonNull(header)) {
            return header.getReceived();
        }
        return null;
    }

    /**
     * 获取端口
     * 同上
     *
     * @param requestEvent
     * @return
     */
    public Integer getPortFromRequest(RequestEvent requestEvent) {
        ViaHeader header = (ViaHeader) requestEvent.getRequest().getHeader(ViaHeader.NAME);
        if (Objects.nonNull(header)) {
            return header.getRPort();
        }
        return null;
    }

    /**
     * 获取请求设备号
     *
     * @param requestEvent
     * @return
     */
    public String getDeviceIdFromRequest(RequestEvent requestEvent) {
        FromHeader fromHeader = (FromHeader) requestEvent.getRequest().getHeader(FromHeader.NAME);
        SipURI toUri = (SipURI) fromHeader.getAddress().getURI();
        return toUri.getUser();
    }

    /**
     * 判断是否认证
     *
     * @return true 代表未认证false 已经认证
     */
    public boolean isAuth(RequestEvent requestEvent) {
        Request request = requestEvent.getRequest();
        //获取认证请求头对象
        AuthorizationHeader authorizationHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
        return Objects.isNull(authorizationHeader);
    }

    /**
     * 是否携带认证信息 认证通过
     *
     * @param requestEvent
     * @return
     */
    public boolean isAuthorizationPass(RequestEvent requestEvent, String password) {
        if (isAuth(requestEvent)) {
            return false;
        }
        AuthorizationHeader authorizationHeader = (AuthorizationHeader) requestEvent.getRequest().getHeader(AuthorizationHeader.NAME);
        if (Objects.isNull(authorizationHeader)) {
            log.error("Authorization信息不全，无法认证。");
            return false;
        }
        String username = authorizationHeader.getUsername();
        String realm = authorizationHeader.getRealm();
        String nonce = authorizationHeader.getNonce();
        URI uri = authorizationHeader.getURI();
        String res = authorizationHeader.getResponse();
        String algorithm = authorizationHeader.getAlgorithm();
        if (null == username || null == realm || null == nonce || null == uri || null == res || null == algorithm) {
            log.error("Authorization信息不全，无法认证。");
            return false;
        } else {
            // 比较Authorization信息正确性
            String A1 = MD5Util.MD5(username + CommonConstant.COLON + realm + CommonConstant.COLON + password);
            String A2 = MD5Util.MD5(SipMethodEnum.REGISTER.getName() + CommonConstant.COLON + uri);
            String resStr = MD5Util.MD5(A1 + CommonConstant.COLON + nonce + CommonConstant.COLON + A2);
            return resStr.equals(res);
        }
    }


    /**
     * 响应 200 ok
     *
     * @param requestEvent
     */
    public void doSuccess(RequestEvent requestEvent) throws SipException, ParseException, InvalidArgumentException {
        Request request = requestEvent.getRequest();
        Response response = getMessageFactory().createResponse(Response.OK, request);
        DateHeader dateHeader = getHeaderFactory().createDateHeader(Calendar.getInstance());
        response.addHeader(dateHeader);
        ServerTransaction serverTransactionId = getServerTransaction(requestEvent);
        serverTransactionId.sendResponse(response);
    }

    /**
     * 响应 401
     *
     * @param requestEvent
     */
    public void doUnAuthorized401(RequestEvent requestEvent) throws SipException, ParseException, InvalidArgumentException {
        Response response = getMessageFactory().createResponse(Response.UNAUTHORIZED, requestEvent.getRequest());
        String realm = MD5Util.generateShortUUID();
        String callId = getCallIdFromRequest(requestEvent);
        String nonce = MD5Util.MD5(callId + getDeviceIdFromRequest(requestEvent));
        WWWAuthenticateHeader wwwAuthenticateHeader = getHeaderFactory().createWWWAuthenticateHeader("Digest realm=\"" + realm + "\",nonce=\"" + nonce + "\",algorithm=MD5");
        response.setHeader(wwwAuthenticateHeader);
        ServerTransaction serverTransactionId = getServerTransaction(requestEvent);
        serverTransactionId.sendResponse(response);
    }

    /**
     * 登录认证失败 响应403
     *
     * @param requestEvent
     */
    public void doLoginFail403(RequestEvent requestEvent) throws SipException, ParseException, InvalidArgumentException {
        Request request = requestEvent.getRequest();
        Response response = getMessageFactory().createResponse(Response.FORBIDDEN, request);
        DateHeader dateHeader = getHeaderFactory().createDateHeader(Calendar.getInstance());
        response.addHeader(dateHeader);
        ServerTransaction serverTransactionId = getServerTransaction(requestEvent);
        serverTransactionId.sendResponse(response);
    }

    /**
     * 获取callId
     *
     * @param requestEvent
     * @return
     */
    public String getCallIdFromRequest(RequestEvent requestEvent) {
        CallIdHeader callIdHeader = (CallIdHeader) requestEvent.getRequest().getHeader(CallIdHeader.NAME);
        return callIdHeader.getCallId();
    }

    /**
     * 根据请求获取callId 头
     *
     * @param requestEvent
     * @return
     */
    public CallIdHeader getcallIdHeaderFromRequest(RequestEvent requestEvent) {
        return SipConstant.TRANSPORT_TCP.equals(getTransactionFormRequest(requestEvent)) ? tcpSipProvider.getNewCallId() : udpSipProvider.getNewCallId();
    }

    /**
     * 根据请求获取callId 头
     *
     * @param transaction
     * @return
     */
    public CallIdHeader getcallIdHeaderFromRequest(String transaction) {
        return SipConstant.TRANSPORT_TCP.equals(transaction) ? tcpSipProvider.getNewCallId() : udpSipProvider.getNewCallId();
    }

    /**
     * 根据请求获取 协议类型
     *
     * @return
     */
    public String getTransactionFormRequest(RequestEvent requestEvent) {
        ViaHeader reqViaHeader = (ViaHeader) requestEvent.getRequest().getHeader(ViaHeader.NAME);
        return reqViaHeader.getTransport();
    }

    /**
     * 根据协议类型创建客户端
     *
     * @param transaction
     * @return
     */
    public ClientTransaction getClientTransaction(String transaction, Request request) {
        ClientTransaction clientTransaction = null;
        try {
            if (SipConstant.TRANSPORT_TCP.equals(transaction)) {
                clientTransaction = tcpSipProvider.getNewClientTransaction(request);
            } else if (SipConstant.TRANSPORT_UDP.equals(transaction)) {
                clientTransaction = udpSipProvider.getNewClientTransaction(request);
            }
        } catch (Exception e) {
            log.error("创建客户端发生异常:{}", e.getMessage());
            e.printStackTrace();
        }
        return clientTransaction;
    }

    /**
     * 根据 RequestEvent 获取 ServerTransaction
     *
     * @param requestEvent
     * @return
     */
    public ServerTransaction getServerTransaction(RequestEvent requestEvent) {
        Request request = requestEvent.getRequest();
        ServerTransaction serverTransaction = requestEvent.getServerTransaction();
        String transport = getTransactionFormRequest(requestEvent);
        if (serverTransaction == null) {
            return this.getServerTransaction(transport, request);
        }
        return serverTransaction;
    }

    /**
     * 根据 RequestEvent 获取 ServerTransaction
     *
     * @return
     */
    public ServerTransaction getServerTransaction(String transaction, Request request) {
        ServerTransaction serverTransaction = null;
        // 判断TCP还是UDP
        boolean isTcp = false;
        if (SipConstant.TRANSPORT_TCP.equals(transaction)) {
            isTcp = true;
        }
        try {
            if (isTcp) {
                SipStackImpl stack = (SipStackImpl) tcpSipProvider.getSipStack();
                serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest) request, true);
                if (serverTransaction == null) {
                    serverTransaction = tcpSipProvider.getNewServerTransaction(request);
                }
            } else {
                SipStackImpl stack = (SipStackImpl) udpSipProvider.getSipStack();
                serverTransaction = (SIPServerTransaction) stack.findTransaction((SIPRequest) request, true);
                if (serverTransaction == null) {
                    serverTransaction = udpSipProvider.getNewServerTransaction(request);
                }
            }
        } catch (TransactionAlreadyExistsException e) {
            log.error(e.getMessage());
        } catch (TransactionUnavailableException e) {
            log.error(e.getMessage());
        }
        return serverTransaction;
    }
}
